package org.loong.crypto.model.asn1.cms;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.cms.EncryptedContentInfo;

/**
 * The SignedAndEnvelopedData.
 * 
 * <pre>
 * SignedAndEnvelopedData ::= SEQUENCE {
 *     version                 Version,
 *     recipientInfos          RecipientInfos,
 *     digestAlgorithms        DigestAlgorithmIdentifiers,
 *     encryptedContentInfo    EncryptedContentInfo,
 *     certificates            [0] IMPLICIT ExtendedCertificateAndCertificates OPTIONAL,
 *     crls                    [1] IMPLICIT CertificateRevocationLists OPTIONAL,
 *     signerInfos             SignerInfos
 * }
 * 
 * RecipientInfos ::= SET OF RecipientInfo
 *
 * DigestAlgorithmIdentifiers ::= CHOICE {
 *     daSet SET OF DigestAlgorithmIdentifier,
 *     daSequence SEQUENCE OF DigestAlgorithmIdentifier 
 * }
 *
 * EncryptedContentInfo ::= SEQUENCE {
 *    contentType ContentType,
 *    contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
 *    encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
 * }
 * </pre>
 */
public class SignedAndEnvelopedData extends ASN1Object {

    private static final ASN1Integer V1 = new ASN1Integer(1);

    /**
     * 版本号.
     */
    private ASN1Integer version;

    /**
     * 接收者信息集合.
     */
    private ASN1Set recipientInfos;

    /**
     * 消息摘要算法标识符.
     */
    private ASN1Set digestAlgorithms;

    /**
     * 加密内容.
     */
    private EncryptedContentInfo encryptedContentInfo;

    /**
     * 证书集合.
     */
    private ASN1Set certificates;

    /**
     * 证书撤销列表集合.
     */
    private ASN1Set crls;

    /**
     * 签名者集合.
     */
    private ASN1Set signerInfos;

    public SignedAndEnvelopedData(ASN1Set recipientInfos, ASN1Set digestAlgorithms, EncryptedContentInfo encryptedContentInfo, ASN1Set certificates, ASN1Set crls,
            ASN1Set signerInfos) {
        this(V1, recipientInfos, digestAlgorithms, encryptedContentInfo, certificates, crls, signerInfos);
    }

    public SignedAndEnvelopedData(ASN1Integer version, ASN1Set recipientInfos, ASN1Set digestAlgorithms, EncryptedContentInfo encryptedContentInfo, ASN1Set certificates,
            ASN1Set crls, ASN1Set signerInfos) {
        this.version = version;
        this.recipientInfos = recipientInfos;
        this.digestAlgorithms = digestAlgorithms;
        this.encryptedContentInfo = encryptedContentInfo;
        this.certificates = certificates;
        this.crls = crls;
        this.signerInfos = signerInfos;
    }

    public SignedAndEnvelopedData(ASN1Sequence seq) {
        if (seq.size() < 4 || seq.size() > 7) {
            throw new IllegalArgumentException("Bad sequence size: " + seq.size());
        }

        int index = 0;
        this.version = (ASN1Integer) seq.getObjectAt(index++);
        this.recipientInfos = ASN1Set.getInstance(seq.getObjectAt(index++));
        this.digestAlgorithms = ASN1Set.getInstance(seq.getObjectAt(index++));
        this.encryptedContentInfo = EncryptedContentInfo.getInstance(seq.getObjectAt(index++));

        if (seq.size() > 5) {
            if (seq.size() == 6) {
                this.certificates = ASN1Set.getInstance((ASN1TaggedObject) seq.getObjectAt(index++), false);
            } else {
                this.certificates = ASN1Set.getInstance((ASN1TaggedObject) seq.getObjectAt(index++), false);
                this.crls = ASN1Set.getInstance((ASN1TaggedObject) seq.getObjectAt(index++), false);
            }
        }

        this.signerInfos = ASN1Set.getInstance(seq.getObjectAt(index++));
    }

    public static SignedAndEnvelopedData getInstance(Object obj) {
        if (obj instanceof SignedAndEnvelopedData) {
            return (SignedAndEnvelopedData) obj;
        } else if (obj != null) {
            return new SignedAndEnvelopedData(ASN1Sequence.getInstance(obj));
        }

        return null;
    }

    public ASN1Integer getVersion() {
        return version;
    }

    public ASN1Set getRecipientInfos() {
        return recipientInfos;
    }

    public ASN1Set getDigestAlgorithms() {
        return digestAlgorithms;
    }

    public EncryptedContentInfo getEncryptedContentInfo() {
        return encryptedContentInfo;
    }

    public ASN1Set getCertificates() {
        return certificates;
    }

    public ASN1Set getCrls() {
        return crls;
    }

    public ASN1Set getSignerInfos() {
        return signerInfos;
    }

    @Override
    public ASN1Primitive toASN1Primitive() {
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(version);
        v.add(recipientInfos);
        v.add(digestAlgorithms);
        v.add(encryptedContentInfo);

        if (certificates != null) {
            v.add(new DERTaggedObject(false, 0, certificates));
        }

        if (crls != null) {
            v.add(new DERTaggedObject(false, 1, crls));
        }

        v.add(signerInfos);
        return new DERSequence(v);
    }
}
